package no.sachmuld.methodcombination.examples.reflection.combo;

import no.sachmuld.methodcombination.api.BehaviourChain;
import no.sachmuld.methodcombination.api.CrosscuttingBehaviour;
import no.sachmuld.methodcombination.api.Direction;
import no.sachmuld.methodcombination.api.reflection.component.*;

import java.lang.reflect.Method;

/**
 *
 */
public class StandardCrosscutting<B extends CrosscuttingBehaviour, R> extends DelegatingMethodCombination<B, R> {

    @Override
    public StandardCrosscutting<B, R> build(final Class behaviourClass, final Method targetMethod) {

        final Direction direction = Direction.BOTTOM_UP;

        final CrosscuttingAroundMethodCombination<B, R> crosscuttingAround = new CrosscuttingAroundMethodCombination<>();
        final AroundMethodCombination<B, R>             around = new AroundMethodCombination<>(targetMethod.getName(), direction);
        final CrosscuttingBeforeMethodCombination<B>    crosscuttingBefore = new CrosscuttingBeforeMethodCombination<>();
        final BeforeMethodCombination<B>                before = new BeforeMethodCombination();
        final PrimaryMethodCombination<B, R>            primary = new PrimaryMethodCombination<>(direction);
        final AfterMethodCombination<B>                 after = new AfterMethodCombination();
        final CrosscuttingAfterMethodCombination<B>     crosscuttingAfter = new CrosscuttingAfterMethodCombination<>();

        buildChildren(behaviourClass, targetMethod, crosscuttingAround, around, crosscuttingBefore, before, primary, after, crosscuttingAfter);

        executedBy(
                crosscuttingAround.inner(
                        around.inner(
                                new AbstractMethodCombination<B, R>() {

                                    @Override
                                    public R execute(final Object targetObject, BehaviourChain<? extends B> behaviourChain, final Object... parameters) {

                                        crosscuttingBefore.execute(targetObject, behaviourChain, parameters);

                                        before.execute(targetObject, behaviourChain, parameters);

                                        R result = primary.execute(targetObject, behaviourChain, parameters);

                                        after.execute(targetObject, behaviourChain, parameters);

                                        crosscuttingAfter.execute(targetObject, behaviourChain, parameters);

                                        return result;
                                    }
                                })));

        return this;

    }

}
